#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>

#include "log.hpp"
#include "protocol.hpp"

using namespace std;

namespace Server
{
    enum
    {
        USAGE_ERROR = 1,
        SOCKET_ERROR,
        BIND_ERROR,
        LISTEN_ERROR
    };

    class calServer; // 声明
    class ThreadData
    {
    public:
        ThreadData(calServer *self, int &sockfd) : _self(self), _sockfd(sockfd)
        {
        }

    public:
        calServer *_self;
        int _sockfd;
    };

    static const uint16_t g_port = 3000; // 服务器要开放的端口
    static const int g_backlog = 5;
    static const int g_num = 1024;

    typedef function<bool(const Request &req, Response &resp)> func_t;

    void handler(int sockfd, func_t func) // 放在这里，解耦
    {
        std::string inBuffer;
        while (true)
        {
            // 1. 读取数据
            // 1.1 如何保证读取到的是[一个][完整]的请求！——明确报文之间的边界
            // "content_len"\r\n"x op y"\r\n
            std::string req_text;
            if (!recvPackage(sockfd, inBuffer, &req_text))
                return;
            std::cout << "完整报文（带报头）: \n" << req_text << std::endl;// 日志

            // 1.2 走到这里，一定获得了一个完整的请求报文
            std::string req_str;
            if (!deLength(req_text, &req_str))
                return;
            std::cout << "有效载荷（去掉报头）: \n" << req_str << std::endl;// 日志

            // 2. 反序列化
            // 2.1 得到一个结构化的请求
            Request req;
            if (!req.deserialize(req_str))
                return;

            // 3. 计算——业务处理
            // 3.1 得到一个结构化的响应
            Response resp;
            func(req, resp);

            // 4. 序列化
            // 4.1 得到一个“字符串”
            std::string resp_string;
            resp.serialize(&resp_string);
            std::cout << "计算完成的序列化有效载荷: \n" << resp_string << std::endl;// 日志

            // 5. 发送响应
            // 5.1 得到一个回送报文
            std::string send_string = enLength(resp_string);
            std::cout << "计算完成的序列化完整报文: \n" << send_string << std::endl;// 日志
            
            // 5.2 发送
            send(sockfd, send_string.c_str(), send_string.size(), 0); // warning 有问题
        }
    }

    class calServer
    {
    public:
        calServer(const uint16_t port = g_port) : _port(port), _listen_sockfd(-1)
        {
        }
        ~calServer() {}

        void init()
        {
            // 1. 创建套接字
            _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP通信
            if (_listen_sockfd == -1)
            {
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERROR);
            }
            logMessage(NORMAL, "create socket success: %d", _listen_sockfd);

            // 2. bind网络信息
            struct sockaddr_in local;
            bzero(&local, 0);

            local.sin_family = AF_INET;
            local.sin_addr.s_addr = INADDR_ANY;
            local.sin_port = htons(_port);
            /*
            只有创建套接字，绑定这里才需要做主机转网络操作，其他例如recvfrom、sendto时不需要，因为：
            recvfrom和sendto都是系统调用，对于系统调用，用户不必知道是怎样实现的，系统调用自己在内部就会完成主机序列和网络序列的转换。
            而bind的参数是结构体， 绑定的IP,PORT等信息是用户自己手动设置的数据，所有在进行网络通信前，要进行主机序列转网络序列
            */

            int n = bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
            if (n == -1)
            {
                logMessage(FATAL, "bind error");
                exit(BIND_ERROR);
            }
            logMessage(NORMAL, "bind success");

            // 3. TCP面向连接——通信前先建立连接——设置socket为监听状态
            n = listen(_listen_sockfd, g_backlog); // TODO
            if (n == -1)
            {
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERROR);
            }
            logMessage(NORMAL, "listen socket success");
        }
        void start(func_t func)
        {
            /* 服务器一经启动，应该是不受用户登录退出的影响，这样的进程就是守护进程 */

            while (true)
            {
                // 4. server端获取新连接
                struct sockaddr_in from;
                bzero(&from, 0);
                socklen_t fromlen = sizeof(from);

                // sockfd，未来和客户端进行通信的套接字fd
                int sockfd = accept(_listen_sockfd, (struct sockaddr *)&from, &fromlen);
                if (sockfd == -1)
                {
                    logMessage(ERROR, "accept error, to do next accept");
                    continue; // 当前服务错误，不影响运行
                }
                logMessage(NORMAL, "accept a new link success, get new socket: %d", sockfd); // TODO

                // 5. 通信
                pid_t id = fork();
                if (id == 0)
                {
                    // child process

                    // 因为子进程会继承父进程的文件描述符，但这些文件描述符可能并不是被子进程都需要，所以最好关闭不需要的文件描述符
                    close(_listen_sockfd); // 提供服务，不需要监听

                    handler(sockfd, func);

                    close(sockfd);

                    exit(0);
                }
                // father process
                close(sockfd);

                pid_t ret = waitpid(id, nullptr, 0);
                if (ret > 0)
                {
                    logMessage(NORMAL, "wait child process success");
                }

                // version 2 end

                // natstat -alntp：查看tcp相关 all-listen-number-tcp-process
            }
        }

    private:
    private:
        int _listen_sockfd;
        uint16_t _port;
    };
}